package Session

import (
	"errors"
	"haochen233/horm/clause"
	"reflect"
)

//Insert
func (s *Session) Insert(values ...interface{}) (int64, error) {
	recordValues := make([]interface{}, 0)
	for _, value := range values {
		table := s.Model(value).refTable
		s.clause.Set(clause.INSERT, s.refTable.Name, s.refTable.FieldNames)
		recordValues = append(recordValues, table.RecordValues(value))
	}

	s.clause.Set(clause.VALUES, recordValues...)
	sql, vars := s.clause.Build(clause.INSERT, clause.VALUES)
	ret, err := s.Raw(sql, vars...).Exec()
	if err != nil {
		return 0, err
	}

	return ret.RowsAffected()
}

//Find
//Examples Find(&[]Users)
func (s *Session) Find(values interface{}) error {
	destSlice := reflect.Indirect(reflect.ValueOf(values))
	destType := destSlice.Type().Elem()
	table := s.Model(reflect.New(destType).Elem().Interface()).refTable

	s.clause.Set(clause.SELECT, table.Name, table.FieldNames)
	sql, vars := s.clause.Build(clause.SELECT, clause.WHERE, clause.ORDERBY, clause.LIMIT)
	rows, err := s.Raw(sql, vars...).QueryRows()
	if err != nil {
		return err
	}

	for rows.Next() {
		dest := reflect.New(destType).Elem()

		//设置目标的每个字段的地址切片
		var destFieldsAddrs []interface{}
		for _, name := range table.FieldNames {
			destFieldsAddrs = append(destFieldsAddrs, dest.FieldByName(name).Addr().Interface())
		}

		//将结果扫描到dest中
		if err := rows.Scan(destFieldsAddrs...); err != nil {
			return err
		}
		destSlice.Set(reflect.Append(destSlice, dest))
	}
	return rows.Close()
}

//Update
func (s *Session) Update(fields ...interface{}) (int64, error) {
	m, ok := fields[0].(map[string]interface{})
	if !ok {
		m = make(map[string]interface{})
		for i := 0; i < len(fields); i += 2 {
			m[fields[i].(string)] = fields[i+1]
		}
	}

	s.clause.Set(clause.UPDATE, s.refTable.Name, m)
	sql, vars := s.clause.Build(clause.UPDATE, clause.WHERE)
	ret, err := s.Raw(sql, vars...).Exec()
	if err != nil {
		return 0, err
	}
	return ret.RowsAffected()
}

//Delete records with where clause
func (s *Session) Delete() (int64, error) {
	s.clause.Set(clause.DELETE, s.refTable.Name)
	sql, vars := s.clause.Build(clause.DELETE, clause.WHERE)
	ret, err := s.Raw(sql, vars...).Exec()
	if err != nil {
		return 0, err
	}

	return ret.RowsAffected()
}

//COUNT select count(*) from ...
func (s *Session) Count() (int64, error) {
	s.clause.Set(clause.COUNT, s.refTable.Name)
	sql, vars := s.clause.Build(clause.COUNT, clause.WHERE)
	row := s.Raw(sql, vars...).QueryRow()
	var count int64
	if err := row.Scan(&count); err != nil {
		return 0, err
	}

	return count, nil
}

//Limit
//Example: Limit(1)
func (s *Session) Limit(num int) *Session {
	s.clause.Set(clause.LIMIT, num)
	return s
}

//Where
//Example: Where("Name = ?", "Tom")
func (s *Session) Where(desc string, args ...interface{}) *Session {
	var vars []interface{}
	vars = append(vars, desc)
	vars = append(vars, args...)
	s.clause.Set(clause.WHERE, vars...)
	return s
}

//OrderBy
//Example: OrderBy("id Asc")
func (s *Session) OrderBy(desc string) *Session {
	s.clause.Set(clause.ORDERBY, desc)
	return s
}

//First return first select result
func (s *Session) First(dest interface{}) error {
	src := reflect.Indirect(reflect.ValueOf(dest))
	//先使用reflect.New 获取slice指针，在使用Elem方法拿到slice本体
	destSlice := reflect.New(reflect.SliceOf(src.Type())).Elem()

	//使用链式调用实现First方法 session.Limit(1).Find(&destSlice)
	err := s.Limit(1).Find(destSlice.Addr().Interface())
	if err != nil {
		return err
	}

	if destSlice.Len() == 0 {
		return errors.New("Record Not Found")
	}
	src.Set(destSlice.Index(0))
	s.CallMethod(AfterQuery, src.Addr().Interface())

	return nil
}
